package org.myspringmvc.context;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.myspringmvc.stereotype.Controller;
import org.myspringmvc.web.bind.annotation.RequestMapping;
import org.myspringmvc.web.bind.annotation.RequestMethod;
import org.myspringmvc.web.constant.Const;
import org.myspringmvc.web.method.HandlerMethod;
import org.myspringmvc.web.servlet.HandlerAdapter;
import org.myspringmvc.web.servlet.HandlerInterceptor;
import org.myspringmvc.web.servlet.HandlerMapping;
import org.myspringmvc.web.servlet.mvc.RequestMappingInfo;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author 海晨忆
 * @Des 普通项目的Spring容器的context对象
 * @Date 2025/7/17 14:39
 */
public class ApplicationContext {
    private Map<String, Object> beanMap = new HashMap<>();

    public ApplicationContext(String xmlPath) {
        try {
            //解析springmvc的xml文件
            SAXReader reader = new SAXReader();
            Document document = reader.read(new File(xmlPath));
            //组件扫描
            Element componentScanElement = (Element) document.selectSingleNode("/beans/component-scan");
            Map<RequestMappingInfo, HandlerMethod> map = componentScan(componentScanElement);
            //创建视图解析器
            Element viewResolverElement = (Element) document.selectSingleNode("/beans/bean");
            createViewResolver(viewResolverElement);
            //创建拦截器
            Element interceptorsElement = (Element) document.selectSingleNode("/beans/interceptors");
            createInterceptors(interceptorsElement);
            //创建org.myspringmvc.web.servlet.mvc.method.annotation包下所有的HandlerMapping
            createHandlerMapping(Const.DEFAULT_PACKAGE, map);
            //创建org.myspringmvc.web.servlet.mvc.method.annotation包下所有的HandlerAdapter
            createHandlerAdapter(Const.DEFAULT_PACKAGE);
            System.out.println("beanMap:" + beanMap);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 创建HandlerAdapter
     *
     * @param packageName
     */
    private void createHandlerAdapter(String packageName) throws Exception {
        String defaultPath = packageName.replace(".", "/");
        //获取绝对路径
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(defaultPath).getPath();
        absolutePath = URLDecoder.decode(absolutePath, Charset.forName("UTF-8"));
        File[] files = new File(absolutePath).listFiles();
        for (File file : files) {
            String classFileName = file.getName();
            String simpleClassName = classFileName.substring(0, classFileName.lastIndexOf("."));
            System.out.println(simpleClassName);
            String className = packageName + "." + simpleClassName;
            Class<?> clazz = Class.forName(className);
            //如果clazz实现了HandlerAdapter接口
            if (HandlerAdapter.class.isAssignableFrom(clazz)) {
                Object bean = clazz.newInstance();
                beanMap.put(Const.HANDLER_ADAPTER, bean);
                return;
            }
        }
    }

    /**
     * 创建HandlerMapping
     *
     * @param packageName
     */
    private void createHandlerMapping(String packageName, Map<RequestMappingInfo, HandlerMethod> map) throws Exception {
        String defaultPath = packageName.replace(".", "/");
        //获取绝对路径
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(defaultPath).getPath();
        absolutePath = URLDecoder.decode(absolutePath, Charset.forName("UTF-8"));
        File[] files = new File(absolutePath).listFiles();
        for (File file : files) {
            String classFileName = file.getName();
            String simpleClassName = classFileName.substring(0, classFileName.lastIndexOf("."));
            System.out.println(simpleClassName);
            String className = packageName + "." + simpleClassName;
            Class<?> clazz = Class.forName(className);
            //如果clazz实现了HandlerMapping接口
            if (HandlerMapping.class.isAssignableFrom(clazz)) {
//                Object bean = clazz.newInstance();
                //修改后，调用有参数的构造方法
                Constructor<?> con = clazz.getDeclaredConstructor(Map.class);
                Object bean = con.newInstance(map);
                beanMap.put(Const.HANDLER_MAPPING, bean);
                return;
            }
        }
    }

    /**
     * 创建拦截器
     *
     * @param interceptorsElement
     */
    private void createInterceptors(Element interceptorsElement) throws Exception {
        List<HandlerInterceptor> interceptors = new ArrayList<>();
        List<Element> interceptorElements = interceptorsElement.elements("bean");
        for (Element interceptorElement : interceptorElements) {
            String interceptorClassName = interceptorElement.attributeValue("class");
            Class<?> clazz = Class.forName(interceptorClassName);
            Object beanInterceptor = clazz.newInstance();
            interceptors.add((HandlerInterceptor) beanInterceptor);
        }
        beanMap.put(Const.INTERCEPTORS, interceptors);
    }

    /**
     * 创建试图解析器
     *
     * @param viewResolverElement
     */
    private void createViewResolver(Element viewResolverElement) throws Exception {
        String className = viewResolverElement.attributeValue("class");
        System.out.println("ViewResolver:" + className);
        Class<?> clazz = Class.forName(className);
        Object bean = clazz.newInstance();
        List<Element> propertyElements = viewResolverElement.elements("property");
        for (Element propertyElement : propertyElements) {
            String propertyName = propertyElement.attributeValue("name");
            String propertyValue = propertyElement.attributeValue("value");
//            System.out.println("propertyName:" + propertyName);
//            System.out.println("propertyValue:" + propertyValue);
            String methodName = filedNameToSetMethodName(propertyName);
            Method setMethod = bean.getClass().getDeclaredMethod(methodName, String.class);
            setMethod.invoke(bean, propertyValue);
        }
        beanMap.put(Const.VIEW_RESOLVER, bean);
    }

    /**
     * 属性名转换为set方法名
     *
     * @param fileName
     * @return
     */
    private String filedNameToSetMethodName(String fileName) {
        return "set" + fileName.substring(0, 1).toUpperCase() + fileName.substring(1);
    }

    /**
     * 组件扫描
     *
     * @param componentScanElement
     */
    private Map<RequestMappingInfo, HandlerMethod> componentScan(Element componentScanElement) throws Exception {
        Map<RequestMappingInfo, HandlerMethod> map = new HashMap<>();
        //获取包名
        String basePackage = componentScanElement.attributeValue(Const.BASE_PACKAGE);
        System.out.println("basePackage:" + basePackage);
        //获取包路径
        String basePath = basePackage.replace(".", "/");
        System.out.println("basePath:" + basePath);
        //获取绝对路径
        String absolutePath = Thread.currentThread().getContextClassLoader().getResource(basePath).getPath();
        absolutePath = URLDecoder.decode(absolutePath, Charset.defaultCharset());
        System.out.println("absolutePath:" + absolutePath);
        //获取路径下的所有文件
        File file = new File(absolutePath);
        File[] files = file.listFiles();
        for (File f : files) {
            String fName = f.getName();
            System.out.println("fName:" + fName);
            if (fName.endsWith(Const.SUFFIX_CLASS)) {
                String simpleClassName = fName.substring(0, fName.length() - Const.SUFFIX_CLASS.length());
                String className = basePackage + "." + simpleClassName;
                System.out.println("className:" + className);
                //通过反射获取对象
                Class<?> clazz = Class.forName(className);
                //查看对象上有没有@Controller注解，有则存放到IOC容器当中
                if (clazz.isAnnotationPresent(Controller.class)) {
                    //创建Controller对象
                    Object bean = clazz.newInstance();
                    System.out.println(bean);
                    beanMap.put(firstCharLowerCase(simpleClassName), bean);
                    //创建这个bean中所有的HandlerMethod对象，将其放到map集合中
                    for (Method method : clazz.getDeclaredMethods()) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            //创建RequestMappingInfo对象
                            RequestMappingInfo requestMappingInfo = new RequestMappingInfo();
                            //获取注解对象，获取注解对象上的值
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            String uri = requestMapping.value()[0];
                            requestMappingInfo.setRequestURI(uri);//请求路径
                            RequestMethod requestMethod = requestMapping.method();
                            requestMappingInfo.setMethod(requestMethod.toString());//请求方式
                            //创建HandlerMethod对象
                            HandlerMethod handlerMethod = new HandlerMethod();
                            handlerMethod.setHandler(bean);//真正的Controller对象
                            handlerMethod.setMethod(method);//标注的方法
                            map.put(requestMappingInfo, handlerMethod);
                        }
                    }
                }
            }
        }
        return map;
    }

    /**
     * 字符串首字母小写
     *
     * @param simpleClassName
     * @return
     */
    private String firstCharLowerCase(String simpleClassName) {
        return simpleClassName.substring(0, 1).toLowerCase() + simpleClassName.substring(1);
    }

    /**
     * 通过beanName获取bean对象
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        return beanMap.get(beanName);
    }
}
